
/*
=====================================================================
  LEX.C: lexical analyzer for ANSI C parser
  Verion 1.0
  By Xing Liu; Extensions and error recovery by Jeff Taylor
  Copyright(c) Abraxas Software Inc. (R), 1988-1989, All rights reserved

=====================================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "errorlib.h"
#include "global.h"
#include "ansic.h"

int c;
FILE *fin;

#define DIM(a)	(sizeof(a)/sizeof((a)[0]))
#define TABLESZ 256
#define IDENTSZ 64

char types[TABLESZ][IDENTSZ];
char enums[TABLESZ][IDENTSZ];
int  ntypes = 0;
int  nenums = 0;
char tokval[BUFSIZE];

/* finddef - find definition in symbol table */
int finddef(tab, limit, ident)
char tab[TABLESZ][IDENTSZ];
int  limit;
char *ident;
{
  register int i;

  for (i = 0; i < limit; i++) {
    if (strcmp(ident, tab[i]) == 0)
      return (TRUE);
  }
  return (FALSE);
}


/* adddef - add definition to symbol table */
void adddef(tab, count, ident)
char tab[TABLESZ][IDENTSZ];
int *count;
char *ident;
{
  if (*count >= TABLESZ) {
    printf("internal name table overflow, abort\n");
    exit(EXIT_FAILURE);
  }
  if (! finddef(tab, *count, ident))
    strcpy(tab[(*count)++], ident);
}

/* yylex - lexical analyzer */
yylex()
{
  int   i, c1, c2, done;
  int   toktype, intval;
  float realval;

  while (isspace(c)) {		/* skip whitespace */
    if (c != '\n')
      c = getc(fin);
    else {		/* at line boundary */
      yylineno++;
      c = getc(fin);
      if (c == '#') {		/* preprocessor line? */
        /* discard rest of line */
        do {
 	  c = getc(fin);
	} while (c != '\n');
      }
    }
  }
  switch (c) {
  case EOF:		/* End Of File */
    return (0);
  case '/':		/* a possible comment */
    c1 = getc(fin);
    if (c1 == '*') {		/* is a comment, skip it */
      done = FALSE;
      do {
        c1 = getc(fin);
        if (c1 == EOF) {
          yyerror("EOF inside comments", NULL);
          exit(1);
        } else if (c1 == '*') {
          c1 = getc(fin);
          if (c1 == '/')
	    done = TRUE;
	  else if (c1 == '\n')		/* at line boundary */
	    ++yylineno;
        } else if (c1 == '\n')		/* at line boundary */
	  ++yylineno;
      } while (! done);
      c = getc(fin);	/* 'c' is lookahead character beyond this token */
      return (yylex());
    } else if (c1 == '=') {	/* not a comment, is it a DIVEQ? */
      toktype = DIVEQ;
      c = getc(fin);
    } else {			/* not a DIVEQ either */
      toktype = '/';
      c = c1;
    }
    break;
  case '"':	/* a string literal */
  case '\'':	/* a char literal */
    i = 0;
    c1 = getc(fin);
    while (c1 != c) {
      if (c1 != '\\')		/* not escape sequence? */
        tokval[i++] = (char) c1;
      else {			/* escape sequence */
        c1 = getc(fin);
        if (c1 != '\n') {	/* escaped newline? */
          tokval[i++] = '\\';
          tokval[i++] = (char) c1;
        }
      }
      c1 = getc(fin);
    }
    tokval[i] = '\0';
    yylval.s = tokval;
    toktype = (c == '"') ? STRING : CHARACTER_CONSTANT;
    c = getc(fin);
    break;
  case '<':
    c1 = getc(fin);
    if (c1 == '=') {		/* less than or equal comparison? */
      toktype = LEQ;
      c = getc(fin);
    } else if (c1 == '<') {
      c2 = getc(fin);
      if (c2 == '=') {		/* left shift assignment? */
        toktype = SHLEQ;
        c = getc(fin);
      } else {			/* left shift */
        toktype = SHL;
        c = c2;
      }
    } else {			/* less than comparison operator */
      toktype = '<';
      c = c1;
    }
    break;
  case '>':
    c1 = getc(fin);
    if (c1 == '=') {
      toktype = GEQ;		/* greater than or equal comparison op */
      c = getc(fin);
    } else if (c1 == '>') {
      c2 = getc(fin);
      if (c2 == '=') {
        toktype = SHREQ;	/* right shift assignment */
        c = getc(fin);
      } else {
        toktype = SHR;		/* right shift */
        c = c2;
      }
    } else {
      toktype = '>';		/* less than comparison */
      c = c1;
    }
    break;
  case '&':
    c1 = getc(fin);
    if (c1 == '&') {
      toktype = ANDAND;		/* logical AND */
      c = getc(fin);
    } else if (c1 == '=') {
      toktype = ANDEQ;		/* bitwise AND assignment */
      c = getc(fin);
    } else {
      toktype = '&';		/* bitwise AND */
      c = c1;
    }
    break;
  case '|':
    c1 = getc(fin);
    if (c1 == '|') {
      toktype = OROR;		/* logical OR */
      c = getc(fin);
    } else if (c1 == '=') {
      toktype = IOREQ;		/* bitwise OR assignment */
      c = getc(fin);
    } else {
      toktype = '|';		/* bitwise OR */
      c = c1;
    }
    break;
  case '^':
    c1 = getc(fin);
    if (c1 == '=') {
      toktype = XOREQ;		/* bitwise exclusive OR assignment */
      c = getc(fin);
    } else {
      toktype = '^';		/* bitwise exclusive OR */
      c = c1;
    }
    break;
  case '!':
    c1 = getc(fin);
    if (c1 == '=') {
      toktype = NEQ;		/* not equal comparison */
      c = getc(fin);
    } else {
      toktype = '!';		/* logical NOT */
      c = c1;
    }
    break;
  case '=':
    c1 = getc(fin);
    if (c1 == '=') {		/* equal comparison */
      toktype = EQU;
      c = getc(fin);
    } else {
      toktype = '=';		/* assignment */
      c = c1;
    }
    break;
  case '+':
    c1 = getc(fin);
    if (c1 == '+') {
      toktype = ADDADD;		/* increment unary operator */
      c = getc(fin);
    } else if (c1 == '=') {
      toktype = ADDEQ;		/* additive assignment */
      c = getc(fin);
    } else {
      toktype = '+';		/* unary plus or binary addition */
      c = c1;
    }
    break;
  case '-':
    c1 = getc(fin);
    if (c1 == '-') {
      toktype = SUBSUB;		/* decrement unary operator */
      c = getc(fin);
    } else if (c1 == '=') {	/* subtractive assignment */
      toktype = SUBEQ;
      c = getc(fin);
    } else if (c1 == '>') {	/* structure dereference */
      toktype = PTR;
      c = getc(fin);
    } else {
      toktype = '-';		/* unary minus or binary subtract */
      c = c1;
    }
    break;
  case '*':
    c1 = getc(fin);
    if (c1 == '=') {
      toktype = MULEQ;		/* multiply assignment */
      c = getc(fin);
    } else {
      toktype = '*';		/* uanry pointer dereference or multiply */
      c = c1;
    }
    break;
  case '%':
    c1 = getc(fin);
    if (c1 == '=') {
      toktype = MODEQ;		/* modulo assignment */
      c = getc(fin);
    } else {
      toktype = '%';		/* modulo */
      c = c1;
    }
    break;
  case '.':
    c1 = getc(fin);
    if (c1 == '.') {
      c2 = getc(fin);
      if (c2 == '.') {
        toktype = DDD;		/* ellipsis for prototypes */
        c = getc(fin);
      } else {
        ungetc(c2, fin);
        toktype = '.';		/* structure/union field */
        c = c1;
      }
    } else {
      toktype = '.';		/* structure/union field */
      c = c1;
    }
    break;
  default:
    if (isdigit(c)) {			/* number? */
      toktype = INTEGER_CONSTANT;
      intval = 0;
      do {
        intval = intval * 10 + (c - '0');
        c = getc(fin);
      } while (isdigit(c));
      if (c == '.') {			/* floating point constant */
        toktype = FLOAT_CONSTANT;
        realval = 0.0;
        i = 10;
        c = getc(fin);
        while (isdigit(c)) {
          realval = realval + (float) (c - '0') / i;
          i = i * 10;
          c = getc(fin);
        }
      }
      if (toktype == INTEGER_CONSTANT)
        yylval.i = intval;
      else
        yylval.r = realval + intval;
    } else if (isalpha(c) || c == '_') {	/* identifier or keyword */
      i = 0;
      do {
        tokval[i++] = (char) c;
        c = getc(fin);
      } while (isalnum(c) || c == '_');
      tokval[i++] = '\0';
      yylval.s = tokval;
      toktype = findname(tokval);
      if (toktype == IDENTIFIER) {	/* not keyword? */
        if (finddef(types, ntypes, tokval))
          toktype = TYPENAME;
        else if (finddef(enums, nenums, tokval))
          toktype = ENUMERATION_CONSTANT;
      }
    } else {		/* all other single character operators */
      toktype = c;
      c = getc(fin);
    }
  }
  return (toktype);
}

struct {		/* C keywords */
  char name[16];
  int val;
} names[] ={
		"auto",		Auto,
		"break",	Break,
		"case",		Case,
		"char",		Char,
		"const",	Const,
		"continue",	Continue,
		"default",	Default,
		"do",		Do,
		"double",	Double,
		"else",		Else,
		"enum",		Enum,
		"extern",	Extern,
		"float",	Float,
		"for",		For,
		"goto",		Goto,
		"if",		If,
		"int",		Int,
		"long",		Long,
		"register",	Register,
		"return",	Return,
		"short",	Short,
		"signed",	Signed,
		"sizeof",	Sizeof,
		"static",	Static,
		"struct",	Struct,
		"switch",	Switch,
		"typedef",	Typedef,
		"volatile",	Volatile,
		"union",	Union,
		"unsigned",	Unsigned,
		"void",		Void,
		"while",	While,
};

int findname(s)
char *s;
{
  int hi, lo, mid;
  int cc;

  /* binary search */
  lo = 0;
  hi = DIM(names) - 1;
  while (lo <= hi) {
    mid = (lo + hi) / 2;
    if ((cc = strcmp(s, names[mid].name)) == 0)
      return (names[mid].val);
    if (cc < 0)
      hi = mid - 1;
    else
      lo = mid + 1;
  }
  return (IDENTIFIER);
}
